///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/fqmc/configuration.cpp
///
/// \brief   implementation of members for the Configuration and ConfigDiff classes
///
///////////////////////////////////////////////////////////////////////////////////////////////////

//#include<algorithm>
//
//#include<formic/fqmc/configuration.h>
//#include<formic/random/random.h>
//#include<formic/input/base.h>
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief default constructor
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//formic::ConfigDiff::ConfigDiff()
//  : _newly_empty(),
//    _newly_full()
//{}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief Constructs the configuration difference object
/////
///// \param[in]      userinp  the user's input options
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//formic::ConfigDiff::ConfigDiff(const formic::InputBase & userinp)
//  : _newly_empty(),
//    _newly_full()
//{}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief  constructor from two configurations
/////
///// \param[in]      new_config     the new configuration
///// \param[in]      old_config     the old configuration
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//formic::ConfigDiff::ConfigDiff(const formic::Configuration & new_config, const formic::Configuration & old_config)
//  : _newly_empty(),
//    _newly_full()
//{
//
//  // ensure the fock vectors are the same lengths
//  if ( new_config.fock_config().size() != old_config.fock_config().size() )
//    throw formic::Exception("fock vector size mismatch in ConfgDiff constructor");
//
//  // process alpha and beta sites separately
//  for (int b = 0; b < 2; b++) {
//
//    // check for sites for which the occupation changed
//    for (int i = b; i < new_config.fock_config().size(); i+=2)
//      if (new_config[i] == 0 && old_config[i] == 1)
//        _newly_empty.push_back(i);
//      else if (new_config[i] == 1 && old_config[i] == 0)
//        _newly_full.push_back(i);
//
//    // ensure there were the same number of particles created as destroyed
//    if ( _newly_empty.size() != _newly_full.size() )
//      throw formic::Exception("uneven number of newly empty/full sites for %s particles in ConfgDiff constructor")
//            % ( b == 0 ? "alpha" : "beta" );
//
//  }
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief  return a compound index for a pair of sites, one occupied and the other empty
/////
///// \param[in]     occ_site     the index of the occupied site (in alpha and beta indexing)
///// \param[in]     unocc_site   the index of the unoccupied site (in alpha and beta indexing)
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//int formic::Configuration::occ_unocc_cmpd(int occ_site, int unocc_site) const {
//
//  const int b = occ_site % 2;
//
//  assert( occ_site % 2 == unocc_site % 2 );
//
//  const std::vector<int> & site_to_occ   = ( b == 0 ? _a_site_to_occ   : _b_site_to_occ   );
//  const std::vector<int> & site_to_unocc = ( b == 0 ? _a_site_to_unocc : _b_site_to_unocc );
//  const int np = ( b == 0 ? _nap : _nbp );
//  const int nu = ( b == 0 ? _nau : _nbu );
//
//  occ_site /= 2;
//  unocc_site /= 2;
//
//  assert( site_to_occ[occ_site] >= 0 );
//  assert( site_to_occ[occ_site] < np );
//
//  int retval;
//
//  if ( occ_site == unocc_site ) {
//
//    retval = site_to_occ[occ_site];
//
//  } else {
//
//    assert( site_to_unocc[unocc_site] >= 0 );
//    assert( site_to_unocc[unocc_site] < nu );
//
//    retval = np + nu * site_to_occ[occ_site] + site_to_unocc[unocc_site];
//
//  }
//
//  assert( retval >= 0 );
//  assert( retval < np*(nu+1) );
//
//  return retval;
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief proposes a move from one configuration to another
/////
///// \param[in]     userinp      the user's input options
///// \param[in]     gen          a random number generator
///// \param[in]     config       the configuration to move from
///// \param[out]    config_diff  on exit, specifies the difference between the configurations
/////
///// \return the proposal density ratio, which is the ratio of the probability of proposing the
/////         current configuration if we are at the new configuration to the probability
/////         of proposing the new configuration if we are at the current configuration.
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//template <class GEN> double formic::Configuration::propose_move(const formic::InputBase & userinp,
//                                                                GEN & gen,
//                                                                const formic::Configuration & config,
//                                                                formic::ConfigDiff & config_diff) {
//
//  // get number of atomic orbitals
//  const int nao = userinp.get<int>("nao");
//
//  // propose a new fock space configuration by moving one alpha or one beta electron
//  const int pos1 = gen() % userinp.get<int>("nsites");
//  const int pos1_occ = config.fock_config().at(pos1);
//  assert(pos1_occ == 0 || pos1_occ == 1);
//  const int b = pos1 % 2;
//  const int n_a_part = ( b == 0 ? userinp.get<int>("nalpha") : userinp.get<int>("nbeta") );
//  const int x = gen() % ( pos1_occ == 1 ? nao - n_a_part : n_a_part );
//  int c = 0;
//  int j = 0;
//  for ( ; j < nao; j++)
//    if (config.fock_config().at(2*j+b) != pos1_occ)
//      if (c++ == x)
//        break;
//  const int pos2 = 2*j+b;
//  const int pos2_occ = config.fock_config().at(pos2);
//  assert(c == x+1);
//  assert(pos2_occ == 0 || pos2_occ == 1);
//  assert(pos1 != pos2);
//  assert(pos1_occ != pos2_occ);
//  assert(pos1 % 2 == pos2 % 2);
//
//  // define the difference
//  config_diff.newly_empty().assign(1, (pos1_occ == 1 ? pos1 : pos2));
//  config_diff.newly_full().assign(1, (pos1_occ == 0 ? pos1 : pos2));
//
//  // for now, the proposal density is symmetric, so return one
//  return 1.0;
//
//}
//
//template double formic::Configuration::propose_move(const formic::InputBase & userinp,
//                                                    formic::LaggedFibonacci<> & gen,
//                                                    const formic::Configuration & config,
//                                                    formic::ConfigDiff & config_diff);
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief Constructs the configuration object
/////
///// \param[in]      userinp  the user's input options
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//formic::Configuration::Configuration(const formic::InputBase & userinp)
//  : _nas(userinp.get<int>("nao")),
//    _nbs(userinp.get<int>("nao")),
//    _nap(userinp.get<int>("nalpha")),
//    _nbp(userinp.get<int>("nbeta")),
//    _nau( _nas - _nap ),
//    _nbu( _nbs - _nbp ),
//    _fock_config(userinp.get<int>("nsites")),
//    _a_site_to_occ(_nas, -1),
//    _b_site_to_occ(_nbs, -1),
//    _a_site_to_unocc(_nas, -1),
//    _b_site_to_unocc(_nbs, -1),
//    _a_occ_to_site(_nap, -1),
//    _b_occ_to_site(_nbp, -1),
//    _a_unocc_to_site(_nau, -1),
//    _b_unocc_to_site(_nbu, -1)
//{
//
//  if ( _nap != _nbp )
//    throw formic::Exception("configuration constructor currently assumes equal number of alpha and beta");
//
//  // set some constants
//  const int nsites = userinp.get<int>("nsites");
//  std::vector<int> ab_n_particles(2, 0);
//  ab_n_particles.at(0) = userinp.get<int>("nalpha");
//  ab_n_particles.at(1) = userinp.get<int>("nbeta");
//
//  // if supplied, start in the user's chosen fock space configuration
//  if (userinp.get<std::vector<int> >("init_config").size() == nsites) {
//
//    _fock_config = userinp.get<std::vector<int> >("init_config");
//
//  // otherwise, generate a random fock space configuration
//  } else {
//
//    // remove all particles from the configuration
//    _fock_config.assign(nsites, 0);
//
//    // place alpha (b == 0) and beta (b == 1) particles
//    for (int b = 0; b <= 1; b++) {
//      for (int i = 0; i < ab_n_particles.at(b); i++) {
//        int x = formic::global_lcg() % (nsites/2 - i);
//        int c = 0;
//        int j = 0;
//        for ( ; j < nsites/2; j++)
//          if (_fock_config.at(2*j+b) == 0)
//            if (c++ == x)
//              break;
//        assert(c == x+1);
//        assert(_fock_config.at(2*j+b) == 0);
//        _fock_config.at(2*j+b) = 1;
//      }
//    }
//
//  }
//
//  // initialize occupation/unoccupation/site mappings
//  this->init_occ_unocc_site();
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief  assignment operator for the Configuration class
/////
///// \param[in]      other    the configuration to copy from
/////
///// \return  a reference to the object
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//formic::Configuration & formic::Configuration::operator=(const formic::Configuration & other) {
//
//  if (&other == this)
//    return *this;
//
//  _nas = other._nas;
//  _nbs = other._nbs;
//  _nap = other._nap;
//  _nbp = other._nbp;
//  _nau = other._nau;
//  _nbu = other._nbu;
//
//  if ( _fock_config.size() != other._fock_config.size() ) _fock_config.resize(other._fock_config.size());
//  if ( _a_site_to_occ.size() != other._a_site_to_occ.size() ) _a_site_to_occ.resize(other._a_site_to_occ.size());
//  if ( _b_site_to_occ.size() != other._b_site_to_occ.size() ) _b_site_to_occ.resize(other._b_site_to_occ.size());
//  if ( _a_site_to_unocc.size() != other._a_site_to_unocc.size() ) _a_site_to_unocc.resize(other._a_site_to_unocc.size());
//  if ( _b_site_to_unocc.size() != other._b_site_to_unocc.size() ) _b_site_to_unocc.resize(other._b_site_to_unocc.size());
//  if ( _a_occ_to_site.size() != other._a_occ_to_site.size() ) _a_occ_to_site.resize(other._a_occ_to_site.size());
//  if ( _b_occ_to_site.size() != other._b_occ_to_site.size() ) _b_occ_to_site.resize(other._b_occ_to_site.size());
//  if ( _a_unocc_to_site.size() != other._a_unocc_to_site.size() ) _a_unocc_to_site.resize(other._a_unocc_to_site.size());
//  if ( _b_unocc_to_site.size() != other._b_unocc_to_site.size() ) _b_unocc_to_site.resize(other._b_unocc_to_site.size());
//
//  std::copy(other._fock_config.begin(), other._fock_config.end(), _fock_config.begin());
//  std::copy(other._a_site_to_occ.begin(), other._a_site_to_occ.end(), _a_site_to_occ.begin());
//  std::copy(other._b_site_to_occ.begin(), other._b_site_to_occ.end(), _b_site_to_occ.begin());
//  std::copy(other._a_site_to_unocc.begin(), other._a_site_to_unocc.end(), _a_site_to_unocc.begin());
//  std::copy(other._b_site_to_unocc.begin(), other._b_site_to_unocc.end(), _b_site_to_unocc.begin());
//  std::copy(other._a_occ_to_site.begin(), other._a_occ_to_site.end(), _a_occ_to_site.begin());
//  std::copy(other._b_occ_to_site.begin(), other._b_occ_to_site.end(), _b_occ_to_site.begin());
//  std::copy(other._a_unocc_to_site.begin(), other._a_unocc_to_site.end(), _a_unocc_to_site.begin());
//  std::copy(other._b_unocc_to_site.begin(), other._b_unocc_to_site.end(), _b_unocc_to_site.begin());
//
//  return *this;
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief  Initializes the occ <--> site and unocc <--> site mappings for the stored configuration
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void formic::Configuration::init_occ_unocc_site() {
//
//  for (int b = 0; b < 2; b++) {
//
//    std::vector<int> & site_to_occ   = ( b == 0 ? _a_site_to_occ   : _b_site_to_occ   );
//    std::vector<int> & site_to_unocc = ( b == 0 ? _a_site_to_unocc : _b_site_to_unocc );
//    std::vector<int> & occ_to_site   = ( b == 0 ? _a_occ_to_site   : _b_occ_to_site   );
//    std::vector<int> & unocc_to_site = ( b == 0 ? _a_unocc_to_site : _b_unocc_to_site );
//    const int ns = ( b == 0 ? _nas : _nbs );
//    const int np = ( b == 0 ? _nap : _nbp );
//    const int nu = ( b == 0 ? _nau : _nbu );
//
//    assert( _fock_config.size() == 2*ns );
//
//    int pc = 0; // particle counter
//    int hc = 0; // hole counter
//
//    int i = b;
//    for ( ; i < _fock_config.size(); i+=2) {
//
//      if ( _fock_config[i] == 1 ) {
//
//        site_to_unocc[i/2] = -1;
//        site_to_occ[i/2] = pc;
//        occ_to_site[pc] = i/2;
//        pc++;
//
//      } else {
//
//        site_to_occ[i/2] = -1;
//        site_to_unocc[i/2] = hc;
//        unocc_to_site[hc] = i/2;
//        hc++;
//
//      }
//
//    }
//
//    assert( i/2 == ns );
//    assert(  pc == np );
//    assert(  hc == nu );
//
//  }
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief  Updates the object to incorperate the supplied difference.
/////
///// \param[in]     diff  specifies the difference between the configuration to update to
/////                      and the current configuration
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void formic::Configuration::update(const formic::ConfigDiff & diff) {
//
//  assert( diff.newly_full().size() == diff.newly_empty().size() );
//
//  for (int i = 0; i < diff.newly_full().size(); i++) {
//
//    int new_occ = diff.newly_full().at(i);
//    int old_occ = diff.newly_empty().at(i);
//
//    assert( new_occ % 2 == old_occ % 2 );
//
//    assert( _fock_config.at(new_occ) == 0 );
//    assert( _fock_config.at(old_occ) == 1 );
//
//    _fock_config.at(new_occ) = 1;
//    _fock_config.at(old_occ) = 0;
//
//    const int b = new_occ % 2;
//
//    std::vector<int> & site_to_occ   = ( b == 0 ? _a_site_to_occ   : _b_site_to_occ   );
//    std::vector<int> & site_to_unocc = ( b == 0 ? _a_site_to_unocc : _b_site_to_unocc );
//    std::vector<int> & occ_to_site   = ( b == 0 ? _a_occ_to_site   : _b_occ_to_site   );
//    std::vector<int> & unocc_to_site = ( b == 0 ? _a_unocc_to_site : _b_unocc_to_site );
//    const int ns = ( b == 0 ? _nas : _nbs );
//    const int np = ( b == 0 ? _nap : _nbp );
//    const int nu = ( b == 0 ? _nau : _nbu );
//
//    new_occ /= 2;
//    old_occ /= 2;
//
//    assert( site_to_occ[old_occ] >= 0 );
//    assert( site_to_occ[old_occ] < np );
//    assert( occ_to_site[ site_to_occ[old_occ] ] == old_occ );
//    occ_to_site[ site_to_occ[old_occ] ] = new_occ;
//    site_to_occ[new_occ] = site_to_occ[old_occ];
//    site_to_occ[old_occ] = -1;
//
//    assert( site_to_unocc[new_occ] >= 0 );
//    assert( site_to_unocc[new_occ] < nu );
//    assert( unocc_to_site[ site_to_unocc[new_occ] ] == new_occ );
//    unocc_to_site[ site_to_unocc[new_occ] ] = old_occ;
//    site_to_unocc[old_occ] = site_to_unocc[new_occ];
//    site_to_unocc[new_occ] = -1;
//
//  }
//
//}
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief checks that using the supplied difference object to update the supplied configuration
/////        is reasonable
/////
///// \param[in]      diff          object describing a change to the configuration
///// \param[in]      config        the configuration
///// \param[in]      calling_func  the name of the function in which this check is performed
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void formic::check_config_update(const formic::ConfigDiff & diff,
//                                 const formic::Configuration & config,
//                                 const std::string & calling_func) {
//
//  // check newly full sites
//  for (int i = 0; i < diff.newly_full().size(); i++) {
//    if ( config[diff.newly_full().at(i)] != 0 )
//      throw formic::Exception("check_config_update failed: cannot move an electron into a site that is already occupied in %s") % calling_func;
//    for (int j = 0; j < i; j++)
//      if ( diff.newly_full().at(i) == diff.newly_full().at(j) )
//        throw formic::Exception("check_config_update failed: cannot move two electrons into the same site in %s") % calling_func;
//  }
//
//  // check newly empty sites
//  for (int i = 0; i < diff.newly_empty().size(); i++) {
//    if ( config[diff.newly_empty().at(i)] == 0 )
//      throw formic::Exception("check_config_update failed: cannot move an electron out of a site that is empty in %s") % calling_func;
//    for (int j = 0; j < i; j++)
//      if ( diff.newly_empty().at(i) == diff.newly_empty().at(j) )
//        throw formic::Exception("check_config_update failed: cannot move two electrons out of the same site in %s") % calling_func;
//  }
//
//}
//
